#include "AnimEvaluator.h"

// ------------------------------------------------------------------------------------------------
// Constructor on a given animation. 
AnimEvaluator::AnimEvaluator( const AnimData* data)
{	
	Anim = data;
	mLastTime = 0.0;
	mLastPositions.resize( Anim->mNumChannels, boost::make_tuple( 0, 0, 0));
	updateTime = -1;		
	pTime = 0;
	accumTime =0;
	AnimationIndex = -1;
	pos = (XMVECTOR*) _aligned_malloc(sizeof(XMVECTOR),16);
	rot = (XMVECTOR*) _aligned_malloc(sizeof(XMVECTOR),16);
	sca = (XMVECTOR*) _aligned_malloc(sizeof(XMVECTOR),16);
	Transforms = NULL;
	transSize = -1;	
}

AnimEvaluator::~AnimEvaluator( void)
{
	_aligned_free(pos);	
	_aligned_free(rot);
	_aligned_free(sca);
	_aligned_free(Transforms);
}


bool AnimEvaluator::Update(DWORD tim)
{
	if(tim == updateTime)
	{
		return false;
	}

	updateTime = tim;
	double ct= tim/1000.0;
	accumTime+=ct;
	pTime=accumTime;

	// extract ticks per second. Assume default value if not given
	double ticksPerSecond = Anim->mTicksPerSecond != 0.0 ? Anim->mTicksPerSecond : 25.0;
	// every following time calculation happens in ticks
	pTime *= ticksPerSecond;

	// map into anim's duration
	double time = 0.0f;
	if( Anim->mDuration > 0.0)
		time = fmod( pTime, Anim->mDuration);

	if( transSize != Anim->mNumChannels)
	{		
		if(transSize  == -1)
		{
			Transforms = (XMMATRIX*) _aligned_malloc(sizeof(XMMATRIX) * Anim->mNumChannels,16);		
		}
		else
		{
			Transforms = (XMMATRIX*) _aligned_realloc(Transforms,sizeof(XMMATRIX) * Anim->mNumChannels,16);		
		}
		transSize = Anim->mNumChannels;
	}

	// calculate the transformations for each animation channel
	for( unsigned int a = 0; a < Anim->mNumChannels; a++)
	{
		const AnimNodeData* channel = Anim->mChannels[a];

		// ******** Position *****
		//*pos = XMVectorSet(0, 0, 0,0);
		if( channel->mNumPositionKeys > 0)
		{
			// Look for present frame number. Search from last position if time is after the last time, else from beginning
			// Should be much quicker than always looking from start for the average use case.
			unsigned int frame = (time >= mLastTime) ? mLastPositions[a].get<0>() : 0;
			while( frame < channel->mNumPositionKeys - 1)
			{
				if( time < channel->mPositionKeys[frame+1].mTime)
					break;
				frame++;
			}

			// interpolate between this frame's value and next frame's value
			unsigned int nextFrame = (frame + 1) % channel->mNumPositionKeys;
			const VectorData& key = channel->mPositionKeys[frame];
			const VectorData& nextKey = channel->mPositionKeys[nextFrame];
			double diffTime = nextKey.mTime - key.mTime;
			if( diffTime < 0.0)
				diffTime += Anim->mDuration;
			if( diffTime > 0)
			{
				float factor = float( (time - key.mTime) / diffTime);				
				*pos = *key.mValue + (*nextKey.mValue - *key.mValue) * factor;
			} 
			else
			{
				*pos = *key.mValue;
			}
			mLastPositions[a].get<0>() = frame;
		}

		// ******** Rotation *********		
		//*rot = XMVectorSet(0, 0, 0,0);
		if( channel->mNumRotationKeys > 0)
		{
			unsigned int frame = (time >= mLastTime) ? mLastPositions[a].get<1>() : 0;
			while( frame < channel->mNumRotationKeys - 1)
			{
				if( time < channel->mRotationKeys[frame+1].mTime)
					break;
				frame++;
			}

			// interpolate between this frame's value and next frame's value
			unsigned int nextFrame = (frame + 1) % channel->mNumRotationKeys;
			const VectorData& key = channel->mRotationKeys[frame];
			const VectorData& nextKey = channel->mRotationKeys[nextFrame];
			double diffTime = nextKey.mTime - key.mTime;
			if( diffTime < 0.0)
				diffTime += Anim->mDuration;
			if( diffTime > 0)
			{
				float factor = float( (time - key.mTime) / diffTime);
				*rot = XMQuaternionSlerp(*key.mValue, *nextKey.mValue, factor);
			} 
			else
			{
				*rot = *key.mValue;
			}

			mLastPositions[a].get<1>() = frame;
		}

		// ******** Scaling **********
		//*sca = XMVectorSet(0, 0, 0,0);
		if( channel->mNumScalingKeys > 0)
		{
			unsigned int frame = (time >= mLastTime) ? mLastPositions[a].get<2>() : 0;
			while( frame < channel->mNumScalingKeys - 1)
			{
				if( time < channel->mScalingKeys[frame+1].mTime)
					break;
				frame++;
			}
			
			*sca = *channel->mScalingKeys[frame].mValue;
			mLastPositions[a].get<2>() = frame;
		}

		// build a transformation matrix from it						
		Transforms[a] = XMMatrixRotationQuaternion(*rot);
		Transforms[a] *= XMMatrixTranslationFromVector(*pos);	
		Transforms[a] *= XMMatrixScalingFromVector(*sca);
		//Transforms[a] = XMMatrixTranspose(Transforms[a]);				
	}

	mLastTime = time;

	return true;
}
